package otaku.gateway.core;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.*;
import otaku.gateway.config.ServerConfig;

import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;

public class NettyHttpServer implements HttpServer {
    private NioEventLoopGroup bossGroup;
    private NioEventLoopGroup workerGroup;
    //可能需要做动态的刷新
    private volatile ServerConfig serverConfig;
    private final AtomicInteger state = new AtomicInteger(INIT_STATE);

    private static final int INIT_STATE = 0;
    private static final int STARTING_STATE = 1;
    private static final int STARTED_STATE = 2;
    private static final int STOPPING_STATE = 3;
    private static final int STOPPED_STATE = 4;

    public NettyHttpServer(ServerConfig serverConfig) {
        this.serverConfig = serverConfig;
    }

    @Override
    public void start() {
        if (!state.compareAndSet(INIT_STATE, STARTING_STATE)) {
            return;
        }
        //这里是关键，需要启动netty，配置好matcher和filter，先简单写，后面再抽出来
        doStartServer();
        state.set(STARTED_STATE);
    }

    private void doStartServer() {
        ServerBootstrap bootstrap = new ServerBootstrap();
        bossGroup = new NioEventLoopGroup(1);
        workerGroup = new NioEventLoopGroup(4);
        bootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel channel) {
                        ChannelPipeline pipeline = channel.pipeline();
                        pipeline.addLast(new HttpServerCodec());
                        pipeline.addLast(new HttpObjectAggregator(100000));
                        pipeline.addLast(new SimpleChannelInboundHandler<FullHttpRequest>() {
                            @Override
                            protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) {
                                System.out.println("gateway receive upstream request:" + request.uri());
                                System.out.println("start matching process...");
                                UpstreamRequestMatcher match = null;
                                for (UpstreamRequestMatcher matcher : serverConfig.getUpstreamRequestMatchers()) {
                                    if (matcher.match(request)) {
                                        match = matcher;
                                        break;
                                    }
                                }
                                if (match == null) {
                                    System.out.println("no matcher found!");
                                    DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.NOT_FOUND);
                                    response.content().writeCharSequence("Not Found", StandardCharsets.UTF_8);
                                    HttpUtil.setContentLength(response, response.content().readableBytes());
                                    ctx.writeAndFlush(response);
                                    return;
                                }
                                FullHttpRequest copyRequest = request.copy();
                                NettyDownStreamClient client = new NettyDownStreamClient(ctx, serverConfig);
                                Optional<InetSocketAddress> target = match.getUpstreamTargets().stream().findAny();
                                client.connect(target.get());
                                client.sendRequest(copyRequest);
                            }
                        });
                    }
                });
        bootstrap.bind(serverConfig.getLocalAddress(), serverConfig.getPort()).addListener(future -> {
            if (future.isSuccess()) {
                System.out.println("server listen at [" + serverConfig.getPort() + "] port");
            } else {
                System.err.println("server listen error: " + future.cause());
            }
        });
    }

    @Override
    public void stop() {
        if (!state.compareAndSet(STARTED_STATE, STOPPING_STATE)) {
            return;
        }
        bossGroup.shutdownGracefully();
        workerGroup.shutdownGracefully();
        state.set(STOPPED_STATE);
    }

    @Override
    public ServerConfig getServerConfig() {
        return serverConfig;
    }

}
